namespace my_string
{
	class string
	{
	public:
		
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin() { return _str; }
		const_iterator begin() const { return _str; }
		iterator end() { return _str + _size; }
		const_iterator end() const { return _str + _size; }

		string(const char* str = "")
			:_str(new char[strlen(str) + 1])
			,_size(strlen(str))
			,_capacity(_size)
		{
			strcpy(_str, str);
		}

		string(const string& s)
		{
			_str = new char[s.capacity() + 1];
			memcpy(_str, s._str, s.size() + 1);
			_size = s.size();
			_capacity = s.capacity();
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		string& operator=(string s)
		{
			swap(s);

			return *this;
		}

		string& operator+=(const char* str)
		{
			reserve(_size + strlen(str));

			append(str);
			return *this;
		}
		string& operator+=(const char& ch)
		{
			push_back(ch);

			return *this;
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size && pos >= 0);
			return _str[pos];
		}
		const char& operator[](size_t pos) const
		{
			assert(pos < _size && pos >= 0);
			return _str[pos];
		}

		const char* c_str() const  { return _str; }
		char* c_str() { return _str; }

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				memcpy(tmp, _str, _size + 1);
				delete[] _str;
				_str = tmp;
				_capacity = n;

				//cout << "reserve(size_t " << n << ")" << endl;
			}
		}

		size_t size() const { return _size; }
		size_t capacity() const { return _capacity; }
		bool empty() const { return _size == 0; }

		void push_back(const char& ch)
		{
			if (_size == _capacity)
				reserve(_capacity == 0 ? 4 : _capacity * 2);

			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		}

		string& append(const string& s)
		{
			for (size_t i = 0; i < s.size(); i++)
			{
				(*this).push_back(s[i]);
			}

			return *this;
		}

		string& append(const char* s)
		{
			size_t i = 0;
			while (s[i])
				(*this).push_back(s[i++]);

			return *this;
		}

		string& append(size_t n, char& ch)
		{
			for (size_t i = 0; i < n; i++)
				(*this).push_back(ch);

			return *this;
		}

		string& insert(size_t pos, const string& s)
		{
			assert(pos <= _size && pos >= 0);

			reserve(_size + s.size());
			size_t n = s.size();

			size_t end = _size + n - 1;
			while (end > pos + n - 1)
			{
				_str[end] = _str[end - n];
				end--;
			}
			size_t tmp = pos;

			for (size_t i = 0; i < s.size(); i++)
				_str[tmp++] = s[i];

			_size += n;
			_str[_size] = '\0';

			return *this;
		}

		string& insert(size_t pos, const char* s)
		{
			assert(pos <= _size && pos >= 0);

			reserve(_size + strlen(s));

			size_t n = strlen(s);
			size_t end = _size + n - 1;
			while (end > pos + n - 1)
			{
				_str[end] = _str[end - n];
				end--;
			}

			size_t tmp = pos;
			for (size_t i = 0; s[i]; i++)
				_str[tmp++] = s[i];

			_size += n;
			_size += n;
			_str[_size] = '\0';

			return *this;
		}

		string& insert(size_t pos, const char& ch, size_t n = 0)
		{
			assert(pos <= _size && pos >= 0);
	
			if (n == 0 || n == 1)
			{
				reserve(_size + 1);
				size_t end = _size;
				while (end > pos)
				{
					_str[end] = _str[end - 1];
					end--;
				}
				_str[pos] = ch;
				_size++;
				_str[_size] = '\0';
			}
			else
			{
				reserve(_size + n);
				size_t end = _size + n - 1;
				while (end > pos + n - 1)
				{
					_str[end] = _str[end - n];
					end--;
				}
				size_t tmp = pos;
				for (size_t i = 0; i < n; i++)
					_str[tmp++] = ch;
				_size += n;
				_str[_size] = '\0';
			}
			return *this;
		}

		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos >= 0 && pos < _size);

			if (len == npos || len > _size - pos)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{      
				size_t begin = pos;
				while (begin < _size - len)
				{
					_str[begin] = _str[begin + len];
					begin++;
				}
				_size -= len;
				_str[_size] = '\0';
			}

			return *this;
		}

		string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size && pos >= 0);
			
			string tmp;

			if (len == npos || len >= _size - pos)
			{
				for (size_t i = pos; i < _size; i++)
				{
					tmp += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < pos + len; i++)
				{
					tmp += _str[i];
				}
			}

			return tmp;
		}

		void clear()
		{
			*_str = '\0';
			_size = 0;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;

		const static size_t npos = -1;
	};

	ostream& operator<<(ostream& out, const string& s)
	{
		for (size_t i = 0; i < s.size(); i++)
			out << s[i];

		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		s.clear();

		char ch;
		size_t i = 0;

		in.get(ch);
		while (ch == ' ' || ch == '\n')
			in.get(ch);
		char buff[128] = { '\0' };

		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			in.get(ch);
		}

		if (i != 0)
			s += buff;

		return in;
	}
}

